package utils

import (
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"os"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gabriel-vasile/mimetype"
)

var (
	ossUtilsTemp *OssUtils
	ossUtilsOnce sync.Once
)

type OssUtils struct {
	bucketName string
	useCli     *s3.S3
}

func NewOssUtils() *OssUtils {
	ossUtilsOnce.Do(func() {
		ossUtilsTemp = &OssUtils{
			bucketName: "file.oss.bucketName",
			useCli:     createCil(),
		}
	})
	return ossUtilsTemp
}

// 构建S3客户端
func createCil() *s3.S3 {
	// 配置
	accessKey := "file.oss.accessKey"
	secretKey := "file.oss.secretKey"
	endpoint := "file.oss.endpoint"

	// 创建一个S3会话
	sess, err := session.NewSession(&aws.Config{
		Credentials:      credentials.NewStaticCredentials(accessKey, secretKey, ""),
		Endpoint:         aws.String(endpoint),
		Region:           aws.String("us-east-1"),
		S3ForcePathStyle: aws.Bool(true), // 设置为true以支持使用MinIO
	})
	if err != nil {
		log.Fatal("Failed to create AWS session", err)
	}

	return s3.New(sess)
}

// PutObject 上传文件
func (m *OssUtils) PutObject(objPath string, file *multipart.FileHeader) error {
	f, err := file.Open()
	if err != nil {
		return fmt.Errorf("PutObject 无法打文件, err: %s", err.Error())
	}
	defer func(f multipart.File) {
		_ = f.Close()
	}(f)

	// 获取文件MIME类型
	mtype, err := mimetype.DetectReader(f)
	if err != nil {
		return fmt.Errorf("exception in obtaining file type, err: %s", err.Error())
	}
	// mimetype 读取文件后会导致文件异常，需要执行seek恢复文件
	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		return fmt.Errorf("can't read the file, err: %s", err.Error())
	}
	// 上传
	_, err = m.useCli.PutObject(&s3.PutObjectInput{
		Bucket:      aws.String(m.bucketName),
		Key:         aws.String(objPath),
		ContentType: aws.String(mtype.String()),
		Body:        f,
	})
	return err
}

// GetObject 下载文件，注意记得关闭文件流 defer svc.GetObject(params).Body.Close()
func (m *OssUtils) GetObject(objPath string) (*s3.GetObjectOutput, error) {
	params := &s3.GetObjectInput{
		Bucket: aws.String(m.bucketName),
		Key:    aws.String(objPath),
	}
	return m.useCli.GetObject(params)
}

// DelObject 删除文件
func (m *OssUtils) DelObject(objPath string) error {
	_, err := m.useCli.DeleteObject(&s3.DeleteObjectInput{
		Bucket: aws.String(m.bucketName),
		Key:    aws.String(objPath),
	})
	return err
}

// ListBucket 列出所有桶
func (m *OssUtils) ListBucket() (*s3.ListBucketsOutput, error) {
	svc := m.useCli
	listBucketsInput := &s3.ListBucketsInput{}
	return svc.ListBuckets(listBucketsInput)
}

// ListObject 列出桶内所有文件
func (m *OssUtils) ListObject() (*s3.ListObjectsOutput, error) {
	params := &s3.ListObjectsInput{
		Bucket: aws.String(m.bucketName),
	}
	resp, err := m.useCli.ListObjects(params)
	if err != nil {
		return nil, err
	}
	for _, item := range resp.Contents {
		fmt.Println("=============================================")
		fmt.Println("Name:         ", *item.Key)
		fmt.Println("Last modified:", *item.LastModified)
		fmt.Println("Size:         ", *item.Size)
		fmt.Println("Storage class:", *item.StorageClass)
		fmt.Println("=============================================")
	}
	return resp, err
}

// UploadToLocal 上传到本地
func (m *OssUtils) UploadToLocal(dstPath string, file *multipart.FileHeader) (string, error) {
	// 打开上传的文件
	f, err := file.Open()
	if err != nil {
		return "", err
	}
	defer func(f multipart.File) {
		_ = f.Close()
	}(f)
	rootPath := "./uploads"
	folderPath := rootPath + dstPath
	// 检查文件夹是否存在
	if _, err := os.Stat(folderPath); os.IsNotExist(err) {
		// 文件夹不存在，创建文件夹
		err := os.MkdirAll(folderPath, 0755)
		if err != nil {
			return "", err
		}
	}

	// 创建文件
	fileStr := folderPath + "/" + file.Filename
	dst, err := os.Create(fileStr)
	if err != nil {
		return "", err
	}
	defer func(dst *os.File) {
		_ = dst.Close()
	}(dst)

	// 写入文件(将文件内容复制到目标文件中)
	if _, err = io.Copy(dst, f); err != nil {
		return "", err
	}
	return fileStr, nil
}
